#include "ast.hpp"
#include "ast_json_bind.hpp"
#include "my_parser.hpp"
#include "ast.hpp"
#include "parsercc.hpp"
#include "../lexer/lexercc.hpp"
#include <iomanip>
#include <sstream>
#include <iostream>

using std::cin;
using std::cout;
using std::fstream;
using std::ifstream;

std::stringstream preprocessForHomework(istream& in){
    std::stringstream ss;
    string line;
    cin >> std::noskipws;
    auto lower = [](const string &s) {
        string ret;
        for(auto i: s){
            ret.push_back(tolower(i));
        }
        return ret;
    };
    while(std::getline(in, line)){
        ss << lower(line) << "\n";
    }
    return ss;
}

int main() {
    cout << "\n\n";
    ifstream lexIn;
    ifstream grammarIn;
    ifstream fileIn;
    ifstream astConfigIn;
    fstream grammarOut;
    fstream parseOut;
    fstream astJsonOut;
    fstream astTxtOut;
    __OPEN_AND_CHECK(lexIn, "../../testcase/midl_lex.json", false);
    __OPEN_AND_CHECK(grammarIn, "../../testcase/midl_syntax.txt", false);
    __OPEN_AND_CHECK(grammarOut, "../../output/parseTable.csv", true);
    __OPEN_AND_CHECK(fileIn, "../../testcase/midl.txt", false);
    __OPEN_AND_CHECK(parseOut, "../../output/parse_tree.txt", true);
    __OPEN_AND_CHECK(astJsonOut, "../../output/AST.json", true);
    __OPEN_AND_CHECK(astTxtOut, "../../output/AST.txt", true);
    __OPEN_AND_CHECK(astConfigIn, "../../testcase/midl_ast_conf.json", false);
    // ---------------------------------------------------------------------- //
    // Lex
    auto ssin = preprocessForHomework(fileIn);
    MyLexer lexer = buildLexerFromJson(lexIn);
    auto tokens = lexer.lex(ssin);
    // Parse
    MyParser parser;
    parser.createParser(grammarIn);
    parser.printParseTable(grammarOut);
    cout << "Parse table saved at ./output/parseTable.csv\n";
    MyParser::PtrParseTreeNode parseTree = parser.parse(tokens);
    // Print parse tree
    parser.printParseTree(parseOut, parseTree);
    cout << "Parse tree saved at ./output/parse_tree.txt\n";
    // Print AST
    ASTConfig config = ASTConfig::configWithJson(astConfigIn);
    PtrASTNode ast = ASTNode::buildAST(parseTree, config);
    ast->printAST(astTxtOut);
    astJsonOut << std::setw(2) << json_wrap(ast) << endl;
    cout << "AST saved at ../../output/AST.txt\n";
    // ---------------------------------------------------------------------- //
    grammarOut.close();
    grammarIn.close();
    lexIn.close();
    astConfigIn.close();
    fileIn.close();
    parseOut.close();
    astJsonOut.close();
    astTxtOut.close();
    // ---------------------------------------------------------------------- //
    writeParser(parser, lexer, "../../generated/");
}

